package mspbots.timedoctor.client.oauth;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * data.connector.doctor.oauth.V2AuthService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2020/5/6
 */
@Slf4j
@Service
public class V2AuthService extends BaseAuthService {

    private final WebClient webClient;

    public V2AuthService(WebClient.Builder builder) {
        this.webClient = builder.baseUrl("https://api2.timedoctor.com/")
                .defaultHeader("Accept", "application/json")
                .build();
    }

    @Override
    public Mono<String> authToken(Map<String, Object> params) {
        return this.tokenClientGet(params)
                .map(AuthorizationCode::getToken)
                .onErrorResume(err -> Mono.just(err.getMessage()));
    }

    @Override
    protected Mono<String> refreshToken(Long tenantId, AuthorizationCode authorizationCode) {
        return this.tokenClientGet(Map.of("tenantId", tenantId,
                "email", authorizationCode.getEmail(),
                "password", authorizationCode.getPassword()))
                .map(AuthorizationCode::getAccessToken);
    }

    @Override
    public Mono<Boolean> test(Map<String, Object> params) {
        final String sync_id = UUID.randomUUID().toString();
        final long start = System.nanoTime();
        return tokenClientGet(params)
                .flatMap(authCode -> {
                    long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                    log.debug("authCode: {}", authCode);
                    this.sendLog(sync_id, Long.parseLong(params.get("tenantId").toString()), milliseconds, "test_synchronise", SUCCESS_CODE, "", objectMapper.convertValue(authCode, JsonNode.class));
                    return Mono.just(true);
                })
                .onErrorResume(err -> {
                    long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                    this.sendLog(sync_id, Long.parseLong(params.get("tenantId").toString()), milliseconds, "test_synchronise", FAIL_CODE, err.getMessage(), null);
                    return Mono.just(false);
                });

    }

    private Mono<AuthorizationCode> tokenClientGet(Map<String, Object> params) {
        return this.webClient.post()
                .uri(uriBuilder -> uriBuilder.path("/api/1.0/authorization/login").build())
                .bodyValue(params)
                .retrieve()
                .bodyToMono(JsonNode.class)
                .map(jsonNode -> jsonNode.get("data"))
                .map(jsonNode -> AuthorizationCode.builder().token(jsonNode.get("token").asText())
                        .expires(ZonedDateTime.parse(jsonNode.get("expires").asText()).toLocalDateTime())
                        .createdAt(ZonedDateTime.parse(jsonNode.get("lastSeenGlobal").get("updatedAt").asText()).toLocalDateTime())
                        .email(params.get("email").toString())
                        .password(params.get("password").toString())
                        .tenantId(Long.parseLong(params.get("tenantId").toString()))
                        .extend(jsonNode)
                        .build())
                .doOnNext(authCode -> {
                    log.debug("redis set authorization: {}", authCode);
                    this.reactiveRedisTemplate.opsForValue()
                            .set(this.REDIS_KEY_PREFIX + this.REDIS_TOKEN_KEY_PREFIX + params.get("tenantId") + ":authorization",
                                    authCode).subscribe();
                    this.reactiveRedisTemplate.opsForValue()
                            .set(this.REDIS_KEY_PREFIX + this.REDIS_TOKEN_KEY_PREFIX + params.get("tenantId") + ":token",
                                    authCode.getToken(), Duration.between(authCode.getCreatedAt(), authCode.getExpires()))
                            .subscribe();
                });
    }

}
